package gsd.gaudi.client.converter;

import gsd.gaudi.common.GaudiReport;
import gsd.gaudi.common.model.GaudiObject;
import gsd.gaudi.common.model.log.GaudiAccess;
import gsd.gaudi.common.model.log.GaudiEvent;
import gsd.gaudi.common.model.log.GaudiEventSequence;
import gsd.gaudi.common.model.log.GaudiListener;
import gsd.gaudi.common.model.log.GaudiListenerSequence;
import gsd.gaudi.common.model.structure.GaudiGuiStructure;

import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInput;
import java.io.ObjectInputStream;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.util.Calendar;
import java.util.HashMap;
import java.util.LinkedList;

public class GaudiListeners2Events {

	private String guifile;
	private String tracefile;
	
	private GaudiGuiStructure gstructure;
	private GaudiReport report;
	
	private GaudiEventSequence new_seq;
		
	//private LinkedList<String> window_stack;
	
	public GaudiListeners2Events(String guifile, String tracefile) {
		this.guifile = guifile;
		this.tracefile = tracefile;
		this.new_seq = new GaudiEventSequence();
		//this.window_stack = new LinkedList<String>();
	}

	public void convert() {
		
		try{
		
		//System.out.println("[GaudiConverter]-Loading Gui Structure...");
		
		InputStream file = new FileInputStream(guifile);
		InputStream buffer = new BufferedInputStream(file);
		ObjectInput input = new ObjectInputStream (buffer);
		this.gstructure = (GaudiGuiStructure) input.readObject();
		
		//System.out.println("[GaudiConverter]-Loading TraceFile...");
		
		file = new FileInputStream(tracefile);
		buffer = new BufferedInputStream(file);
		input = new ObjectInputStream (buffer);
		this.report = (GaudiReport) input.readObject();
		
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		
		
		//System.out.println("[GaudiConverter]-Converting...");
		
		GaudiListenerSequence sequence = this.report.getListenersequence();
		
		for(GaudiListener l : sequence.getListenerseq()){
			//System.out.println("[GaudiConverter]-Converting the " + sequence.getListenerseq().indexOf(l) + " listener.");
			this.process(l);
		}
		
		
	}

	private void process(GaudiListener l) {
		
		//System.out.println("[GaudiConverter]--->Processing the listener");
		
		String id = this.gstructure.getLister2widget().get(l.getListenerid());
		
		GaudiObject go = this.gstructure.findWidget(id);
		
		this.processAccesses(l.getAccesses());
		
		if(go.getProperties().get("class").equals("javax.swing.JMenuItem")){
			GaudiObject parent = go.getParent();
			if(parent.getProperties().get("class").equals("javax.swing.JMenu")){
				this.generateClick(parent.getId());
			}
		}
		
		this.generateClick(id);
		
	}
		
	private void processAccesses(LinkedList<GaudiAccess> alist){
		//System.out.println("[GaudiConverter]--->Processing the accesses");
		for(GaudiAccess a : alist){
			if(a.isRead()){
				this.processReadAccess(a);
			}
			if(a.isWrite()){
				this.processWriteAccess(a);
			}
		}
	}
	
	private void processReadAccess(GaudiAccess access){
		String target_id = access.getWidgetid();
		
		//System.out.println("[GaudiConverter]--->Processing a read access to " + target_id);
		
		//Process for each type of widget
		
		if(access.getValue() instanceof String && 
				this.gstructure.findWidget(target_id).getProperties().get("class").equals("javax.swing.JTextField")){

			String value = (String) access.getValue();
			//System.out.println("[GaudiConverter]--->Processing a access to a javax.swing.JTextField");
			this.generateSetJTextField(target_id, value);
			
		}else if(access.getValue() instanceof Integer && 
				this.gstructure.findWidget(target_id).getProperties().get("class").equals("javax.swing.JTable")){

			//System.out.println("[GaudiConverter]--->Processing a access to a javax.swing.JTable");
			
			if(((Integer)access.getValue()) == -1)
				return;
			else
				this.generateClick(target_id);
			
		}else{
			//System.out.println("[GaudiConverter]--->Could not process access to a " + this.gstructure.findWidget(target_id).getProperties().get("class"));
		}
	}
	
	private void processWriteAccess(GaudiAccess access){
	
	}
	
	private void generateSetJTextField(String id, String text){
		//System.out.println("[GaudiConverter]--->Generating a JTextFieldEdit");
		
		GaudiEvent newstep = new GaudiEvent(id);
		newstep.delaytime = 1000;
		newstep.id = GaudiEvent.SET_TEXT_JTEXTFIELD;
		newstep.text = text;
		this.new_seq.addEvent(newstep);
	}
	
	private void generateClick(String id){
		//System.out.println("[GaudiConverter]--->Generating a click");
		
		GaudiEvent newstep_press = new GaudiEvent(id);
		newstep_press.delaytime = 1000;
		newstep_press.id = MouseEvent.MOUSE_PRESSED;
		newstep_press.buttonclick = MouseEvent.BUTTON1;
		newstep_press.when = Calendar.getInstance().getTimeInMillis();
		newstep_press.modifiers = MouseEvent.BUTTON1_MASK;
		newstep_press.clickcount = 1;
		
		GaudiEvent newstep_unpress = new GaudiEvent(id);
		newstep_unpress.delaytime = 100;
		newstep_unpress.id = MouseEvent.MOUSE_RELEASED;
		newstep_unpress.buttonclick = MouseEvent.BUTTON1;
		newstep_unpress.when = Calendar.getInstance().getTimeInMillis();
		newstep_unpress.modifiers = MouseEvent.BUTTON1_MASK;
		newstep_unpress.clickcount = 1;
		
		this.new_seq.addEvent(newstep_press);
		this.new_seq.addEvent(newstep_unpress);
	}
	
	private void generateDoubleClick(String id){
		//System.out.println("[GaudiConverter]--->Generating a click");
		
		GaudiEvent newstep_press = new GaudiEvent(id);
		newstep_press.delaytime = 1000;
		newstep_press.id = MouseEvent.MOUSE_PRESSED;
		newstep_press.buttonclick = MouseEvent.BUTTON1;
		newstep_press.when = Calendar.getInstance().getTimeInMillis();
		newstep_press.modifiers = MouseEvent.BUTTON1_MASK;
		newstep_press.clickcount = 2;
		
		GaudiEvent newstep_unpress = new GaudiEvent(id);
		newstep_unpress.delaytime = 10;
		newstep_unpress.id = MouseEvent.MOUSE_RELEASED;
		newstep_unpress.buttonclick = MouseEvent.BUTTON1;
		newstep_unpress.when = Calendar.getInstance().getTimeInMillis();
		newstep_unpress.modifiers = MouseEvent.BUTTON1_MASK;
		newstep_unpress.clickcount = 2;
		
		this.new_seq.addEvent(newstep_press);
		this.new_seq.addEvent(newstep_unpress);
	}
	
	private void generateKeyPress(String id, String ch){
		//System.out.println("[GaudiConverter]--->Generating a keypress");
		
		GaudiEvent newstep_press = new GaudiEvent(id);
		newstep_press.delaytime = 200;
		newstep_press.id = KeyEvent.KEY_PRESSED;
	
		GaudiEvent newstep_unpress = new GaudiEvent(id);
		newstep_unpress.delaytime = 100;
		newstep_unpress.id = KeyEvent.KEY_RELEASED;
		
		try {
			
			if(ch.compareTo(" ") == 0){
				newstep_press.keycode = KeyEvent.VK_SPACE;
				newstep_unpress.keycode = KeyEvent.VK_SPACE;
			}else{
				Field ccode = Class.forName("java.awt.event.KeyEvent").getField("VK_"+ch.toUpperCase().charAt(0));
				newstep_press.keycode = ccode.getInt(ccode);
				newstep_unpress.keycode = newstep_press.keycode;
			}

		} catch (SecurityException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (NoSuchFieldException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		this.new_seq.addEvent(newstep_press);
		this.new_seq.addEvent(newstep_unpress);
	}

	public void recordToFile() {
		
		//System.out.println("[GaudiConverter]-Recording converted trace to file...");
		
		(new File("gaudi")).mkdir();
		(new File("./gaudi/converted")).mkdir();
		
		this.tracefile = this.tracefile.substring(this.tracefile.lastIndexOf("/")+1, this.tracefile.lastIndexOf("."));
		
		String filename = "./gaudi/converted/" + this.tracefile + "_converted.txt";
		
		this.report.setSequence(this.new_seq);
		
		try{
			OutputStream file = new FileOutputStream(filename);
		    OutputStream buffer = new BufferedOutputStream(file);
		    ObjectOutput output = new ObjectOutputStream(buffer);
		    output.writeObject(this.report);
			output.close();
		}catch (Exception e1){
			System.err.println("Error: " + e1.getMessage());
		}
		
		System.out.println("[GaudiConverter]-run recorded in file: " + filename);
		
	}

}
